﻿using System;
using System.Linq.Expressions;
using System.Reflection;

namespace EOrm.NpgsqlProvider
{
    /// <summary>
    /// postgres的排序条件表达式
    /// </summary>
    public class PgOrderByVisitor : OrderByVisitor
    {
        /// <summary>
        /// 生成排序条件
        /// </summary>
        /// <returns>排序条件</returns>
        public override string Combine()
        {
            string res = string.Join(", ", _orderByStack.ToArray());
            _orderByStack.Clear();
            return res;
        }

        /// <summary>
        /// 访问表达式
        /// </summary>
        /// <param name="exp">要访问的表达式</param>
        /// <param name="table">目标表</param>
        /// <exception cref="Exception">参数为空异常</exception>
        public override void Visit(Expression exp, ITable table)
        {
            //验证表达式是否为空
            if (exp == null)
            {
                return;
                //throw new Exception("不能访问空表达式");
            }
            //验证数据表
            if (table == null)
            {
                throw new Exception("数据表结构获取失败");
            }
            //根据表达式的类型处理不同的逻辑
            switch (exp.NodeType)
            {
                //访问兰布达表达式
                case ExpressionType.Lambda:
                    //表达式转换为兰布达表达式
                    LambdaExpression lambdaExp = exp as LambdaExpression;
                    //验证表达式
                    if (lambdaExp == null)
                    {
                        throw new Exception("兰布达表达式为空");
                    }
                    //访问其子表达式
                    Visit(lambdaExp.Body, table);
                    break;
                //访问成员表达式
                case ExpressionType.MemberAccess:
                    //转换为成员表达式
                    MemberExpression memberExp = exp as MemberExpression;
                    //验证成员表达式
                    if (memberExp == null)
                    {
                        throw new Exception("向成员表达式转换失败");
                    }
                    //访问成员表达式
                    VisitMember(memberExp, table);
                    break;
                //访问调用表达式
                case ExpressionType.Call:
                    //转换为调用表达式
                    MethodCallExpression mcExp = exp as MethodCallExpression;
                    //验证调用表达式
                    if (mcExp == null)
                    {
                        throw new Exception("向调用表达式转换失败");
                    }
                    //访问调用表达式
                    VisitMethodCall(mcExp, table);
                    break;
                //访问转换表达式
                case ExpressionType.Convert:
                    //转换为一元表达式
                    UnaryExpression convertUnaryExp = exp as UnaryExpression;
                    //验证转换表达式
                    if (convertUnaryExp == null)
                    {
                        throw new Exception("向转换一元表达式转换失败");
                    }
                    //访问转换一元表达式的子句
                    Visit(convertUnaryExp.Operand, table);
                    break;
                //默认的调用
                default:
                    throw new Exception(string.Format("不支持的表达式类型：{0}", exp.NodeType));
            }
        }

        /// <summary>
        /// 访问成员表达式
        /// </summary>
        /// <param name="exp">成员表达式</param>
        /// <param name="table">目标数据表</param>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量计算异常</exception>
        protected void VisitMember(MemberExpression exp, ITable table)
        {
            //验证表达式
            if (exp == null)
            {
                throw new ArgumentNullException("exp", "成员表达式为空");
            }
            //验证数据表
            if (table == null)
            {
                throw new ArgumentNullException("table", "数据表结构获取失败");
            }
            //获取要访问的对象
            PropertyInfo pi = exp.Member.ReflectedType.GetProperty(exp.Member.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
            //如果要访问的对象为空，则获取表达式的值
            if (pi == null)
            {
                //表达式转换为一元表达式
                UnaryExpression cast = Expression.Convert(exp, typeof(object));
                //获取表达式的值
                object obj = Expression.Lambda<Func<object>>(cast).Compile().Invoke();
                //推入排序字段
                _orderByStack.Push(obj.ToString());
            }
            else
            {
                //查找字段
                IField field = table.Fields.Find(a => a.Name == pi.Name);
                //字段认证
                if (field == null)
                {
                    throw new ArgumentException("没有找到这个字段", "field");
                }
                //推入排序字段
                _orderByStack.Push(field.DbName);
            }
        }

        /// <summary>
        /// 访问方法调用表达式
        /// </summary>
        /// <param name="exp">方法调用表达式</param>
        /// <param name="table">目标数据表</param>
        /// <returns>访问方法调用表达式</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        /// <exception cref="ArgumentException">参数处理异常</exception>
        protected void VisitMethodCall(MethodCallExpression exp, ITable table)
        {
            //验证表达式
            if (exp == null)
            {
                throw new ArgumentNullException("exp", "成员表达式为空");
            }
            //验证数据表
            if (table == null)
            {
                throw new ArgumentNullException("数据表结构获取失败");
            }
            //验证
            if (exp.Arguments == null || exp.Arguments.Count < 2)
            {
                throw new ArgumentException("方法调用表达式参数错误");
            }
            string method = exp.Method.Name;
            if (method == "OrderBy" || method == "OrderByDescending" || method == "ThenBy" || method == "ThenByDescending")
            {
                //访问第二变量
                Visit(exp.Arguments[1], table);
                //弹出上一目标字段
                string lastField = _orderByStack.Pop();
                //压入字段的排序处理方法
                if (method == "OrderBy" || method == "ThenBy")
                {
                    _orderByStack.Push(string.Format("{0} ASC", lastField));
                }
                else
                {
                    _orderByStack.Push(string.Format("{0} DESC", lastField));
                }
            }
            //访问第一变量
            Visit(exp.Arguments[0], table);
        }
    }
}
